﻿#region 참조 네임스페이스

using System;
using System.Collections.Generic;
using System.Text;

#endregion

namespace Skying.Resources
{
	/// <summary>
	/// 문자열 리소스를 구성하는 클래스로 <see cref="Skying.Resources.Resource"/>로부터 파생된다.
	/// </summary>
	public class StringResource : Resource
	{
		#region 상수

		/// <summary>
		/// 한 개의 세그먼트 당 문자열의 개수 상수를 나타낸다.
		/// </summary>
		/// <remarks>
		/// <para>문자열 리소스는 string table 안에 유니코드 문자열로 저장된다. 이 때, 하나의 세그먼트에는 16개의 문자열이 포함될 수 있다.</para>
		/// <para>각 문자열을 고유의 번호를 갖는데, 첫 번째 세그먼트에는 0부터 15에 해당하는 문자열이 포함되고, 두 번째 세그먼트에는 16부터 31에 해당하는 문자열이 포함되며, 그 이상도 마찬가지이다.</para>
		/// <para>자세한 내용은 http://support.microsoft.com/kb/200893/en-us?fr=1 을 참조하라.</para>
		/// </remarks>
		private const int _STRINGS_COUNT_IN_A_SEGMENT = 16;

		#endregion

		#region 프로퍼티

		/// <summary>
		/// 문자열 리소스에 대한 아이디/리소스 값 쌍 목록을 가져온다.
		/// </summary>
		public List<KeyValuePair<int, string>> Values
		{
			get;
			private set;
		}

		#endregion

		#region 생성자/소멸자

		public StringResource(IntPtr moduleHandle, IntPtr type, IntPtr name)
			: base(moduleHandle, type, name)
		{
			
		}

		#endregion

		#region 리소스

		/// <summary>
		/// 문자열 리소스를 구성한다.
		/// </summary>
		public override void CombineResource()
		{
			// 문자열 테이블의 세그먼트에 대한 바이너리를 가져온다.
			byte[] segmentBinary = base.LoadResourceBinary(base.TypeHandle, base.NameHandle);

			if (segmentBinary.Length > 0)
			{
				this.Values = new List<KeyValuePair<int, string>>(_STRINGS_COUNT_IN_A_SEGMENT);

				// 세그먼트에 대한 바이너리를 char[] 형태로 변환한다. 문자열 테이블의 리소스는 유니코드로 저장되므로 변환할 때도 유니코드로 변환한다.
				char[] segmentCharacters = Encoding.Unicode.GetString(segmentBinary).ToCharArray();

				// 세그먼트 바이너리를 탐색하기 위한 오프셋을 나타낸다.
				int offset = 0;

				// 한 개의 세그먼트 내에는 최대 16개의 문자열을 포함할 수 있으므로 그 만큼 세그먼트를 조사한다.
				for (int index = 0; index < _STRINGS_COUNT_IN_A_SEGMENT; index++)
				{
					if (segmentCharacters[offset] == 0)
					{
						offset++;
					}
					else if (segmentCharacters[offset] != 0)
					{
						// 문자열 리소스에 대한 시작점은 해당 문자열 리소스의 길이를 나타낸다.
						int length = segmentCharacters[offset];

						offset++;

						if (length > 0)
						{
							// NameHandle 속성이 세그먼트의 고유 번호가 된다. 첫 번째 세그먼트는 0부터 15까지의 문자열 리소스를 가진다. 그러므로, 다음과 같은 계산식으로 문자열 리소스의 고유 번호를 구한다.
							int stringId = (this.NameHandle.ToInt32() - 1) * 16 + index;

							char[] resourceCharacters = new char[length];

							// 하나의 문자열 리소스를 세그먼트로부터 가져온다.
							Array.Copy(segmentCharacters, offset, resourceCharacters, 0, length);

							offset += length;

							// 문자열 리소스 목록에 추가시킨다.
							this.Values.Add(new KeyValuePair<int, string>(stringId, new string(resourceCharacters)));
						}
					}
				}
			}
		}

		#endregion
	}
}